/**
 *	@file	tr_house_phc.c
 *	@brief	トレーナーハウス　PHCオーバーレイソース
 *	@author	Miyuki Iwasawa
 *	@date	09.03.25
 *
 *	このソースは特別にトレーナーハウスセーブデータの全構造体メンバを公開します
 *	ここで公開していても、他のソースに不用意に公開は禁止
 */

#include "common.h"
#include "savedata/savedata_def.h"
#include "savedata/savedata.h"

#include "system/gamedata.h"
#include "system/pms_data.h"
#include "system/buflen.h"
#include "system/msgdata.h"
#include "system/pm_str.h"
#include "gflib/strbuf.h"
#include "gflib/strbuf_family.h"
#include "battle/battle_common.h"
#include "battle/battle_server.h"

#include "poketool/poke_tool.h"
#include "poketool/pokeparty.h"
#include "savedata/mystatus.h"
#include "savedata/b_tower.h"
#include "savedata/tr_house.h"
#include "savedata/tr_house_data.h"	//include許可制

#include "msgdata/msg.naix"
#include "msgdata/msg_tr_house.h"

#include "tr_house/tr_house_phc.h"

#include "field/eventflag.h"

#define PHC_RECDATA_SIZE		(500)	///<レコードデータは500byte
#define PHC_TRHOUSE_DATA_SIZE	(PHC_RECDATA_SIZE-2)
#define PHC_RECDATA_CHECKSUM_ADRS	(PHC_TRHOUSE_DATA_SIZE)

////////////////////////////////////////////////////////////////////////////////////////
//プロトタイプ
static void player_CorrectMyData(TR_HOUSE_PLAYER* pl,SAVEDATA* save);
static void poke_CorrectMyPokeData(TR_HOUSE_POKEMON* poke_tbl,POKEPARTY* party);
static int sub_GetEnableDataNum(TR_HOUSE_SAVE* th_sv);
static int sub_CheckDeleteIndex(TR_HOUSE_SAVE* th_sv,TR_HOUSE_PLAYER* pl,BOOL* same_f);
static void sub_makeFlagArray(SAVEDATA * savedata, u8 * array);
static void sub_updateFlagArray(SAVEDATA * savedata, const u8 * array);

/**
 *	@brief	PHCのレコードデータバッファに手持ちデータを書き込み
 *	@param	save	in:セーブデータポインタ
 *	@param	buf		out:自分の手持ちデータを収集して書き込むPHC_RECDATA_SIZEのバッファへのポインタ
 *
 *	@com	末尾2byteはチェックサム用領域に利用しているので、PHC_RECDATA_SIZE-2byte分しか好きなデータは詰められない！
 */
void TrHousePHC_SetRecordData(SAVEDATA* save,void* buf)
{
	TR_HOUSE_TRAINER* pt;
	MYSTATUS* my = SaveData_GetMyStatus(save);
	POKEPARTY* party = SaveData_GetTemotiPokemon(save);

	MI_CpuClearFast(buf,PHC_RECDATA_SIZE);

	pt = (TR_HOUSE_TRAINER*)buf;
	TrHouseTrainer_Clear(pt);

	player_CorrectMyData(&pt->player,save);
	poke_CorrectMyPokeData(pt->poke,party);

	//チェックサムを計算して埋め込み
	{
//		u16* key = (u16*)((u32)buf+(u32)PHC_RECDATA_CHECKSUM_ADRS);
		u16* key = (u16*)(&((u8*)buf)[PHC_RECDATA_CHECKSUM_ADRS]);
		*key = CalcTool_CrcCheck(buf,PHC_TRHOUSE_DATA_SIZE);
		OS_Printf("TrHousePhcSave CheckSumKey %d\n",*key);
	}
}

/**
 *	@brief	PHCからトレーナーデータを受け取る
 *
 *	@param	save	io:セーブデータポインタ
 *	@param	buf		in:TR_HOUSE_TRAINER* のポインタ(一人分のレコードデータ本体の先頭ポインタ)
 *
 *	@li	一つずつ押し出しながらセーブするので、10人分10回PHC側から呼ばれる
 *	@li	末尾2byteはチェックサム用領域。壊れてたら無視する
 *	@li	この関数は最大10人分のデータがidx0からidx9に向かって古い順に詰まっていると思って作られています
 *	@li	実はPHC側はidx9からidx0に向かって古い順にデータが詰められているので、
 *		呼び出し側でループを逆に回してもらって、PHC本体とDS側のデータの新旧の整合性をとっています。
 *
 */
void TrHousePHC_TrainerDataSave(SAVEDATA* save,void* buf)
{
	u8 flag_array[TR_HOUSE_TRAINER_MAX];
	u8 set_flag = FALSE;
	int no,num;
	BOOL same_f = FALSE,push_f = FALSE;
	TR_HOUSE_SAVE* th_sv = SaveData_GetTrHouseSave(save);
	TR_HOUSE_TRAINER* pt = (TR_HOUSE_TRAINER*)buf;

	//データが有効かチェック
	if(!TrHouseTrainer_IsEnable(pt)){
		int i;
		num = sub_GetEnableDataNum(th_sv);
		OS_Printf("TrHousePhcSave data num = %d\n",num);
		for(i = 0;i < num;i++){
			OS_Printf("TrHouseSave %d, poke = %d\n",i,th_sv->trainer[i].poke[0].mons_no);
		}
		return;
	}
	//チェックサムを取得
	{
		u16* key = (u16*)(&((u8*)buf)[PHC_RECDATA_CHECKSUM_ADRS]);
		u16  data_key = CalcTool_CrcCheck(buf,PHC_TRHOUSE_DATA_SIZE);
		if(*key != data_key){
			OS_Printf("TrHousePhcSave CheckSum Failed key %d != %d poke=%d\n",
				*key,data_key,pt->poke[0].mons_no);
			return;	//チェックサムが合わないので無視
		}
	}

	//現在の有効データ数を取得
	num = sub_GetEnableDataNum(th_sv);
	//対戦フラグを取得
	sub_makeFlagArray(save, flag_array);

	//押し出しで削除するデータindexを取得
	no = sub_CheckDeleteIndex(th_sv,&pt->player,&same_f);

	OS_Printf("TrHousePhcSave num = %d, push = %d same_f = %d\n",num,no,same_f);
	if((num >= TR_HOUSE_TRAINER_MAX) || (same_f)){
		num -= 1;
		push_f = TRUE;
		set_flag = flag_array[no];	//同じデータの場合、その状態フラグをとっておく
	}
	if(push_f && (no < num)){	//no以降のデータを詰める
		int i;
		for(i = no;i < num;i++){
			th_sv->trainer[i] = th_sv->trainer[i+1];
			flag_array[i] = flag_array[i+1];	//フラグも詰める
		}
		OS_Printf("TrHousePhcSave PushData num = %d, no = %d write_num = %d\n",num,no,(num-no));
	}
	//最後に連結
	MI_CpuCopy8(pt,&th_sv->trainer[num],sizeof(TR_HOUSE_TRAINER));
	OS_Printf("TrHousePhcSave Write num = %d\n",num);
	OS_Printf("  PhcSave srcPoke = %d destPoke = %d\n",pt->poke[0].mons_no,th_sv->trainer[num].poke[0].mons_no);
	//受け取ったトレーナー位置に対応するフラグを設定
	flag_array[num] = set_flag;
	//対戦フラグをアップデート
	sub_updateFlagArray(save, flag_array);
}

/**
 *	@brief	プレイヤーデータを収集するサブ関数
 *
 *	@param	pl		out:プレイヤーのデータを収集して格納するTR_HOUSE_PLAYER型バッファへのポインタ
 *	@param	save	in:セーブデータポインタ
 */
static void player_CorrectMyData(TR_HOUSE_PLAYER* pl,SAVEDATA* save)
{
	int i;
	MYSTATUS* my = SaveData_GetMyStatus(save);

	pl->tr_id = MyStatus_GetID(my);
	pl->tr_sex = MyStatus_GetMySex(my);
	pl->region = MyStatus_GetRegionCode(my);
	pl->version = MyStatus_GetRomCode(my);
	pl->obj_code = MyStatus_GetTrainerView(my);
	PM_strncpy( pl->name, MyStatus_GetMyName(my), PERSON_NAME_SIZE);

	//タワーの簡易会話データを取得
	for(i = 0;i < TR_HOUSE_PMSG_MAX;i++){
		PMS_DATA* pd = TowerPlayerMsg_Get(save,i);
		PMSDAT_Copy( &pl->word[i], TowerPlayerMsg_Get(save,i));
	}
}

/*
 *	@brief	プレイヤーの手持ちポケモンデータを収集するサブ関数
 *
 *	@param	poke_tbl	out:プレイヤーの手持ちポケモンデータを格納するsizeof(TR_HOUSE_POKEMON)*6のサイズを持つバッファポインタ
 *	@param	party		in:手持ちのPOKEPARTY構造体の参照ポインタ
 */
static void poke_CorrectMyPokeData(TR_HOUSE_POKEMON* poke_tbl,POKEPARTY* party)
{
	int i,j,num,idx;
	POKEMON_PARAM* pp;
	TR_HOUSE_POKEMON* poke;
	num = PokeParty_GetPokeCount(party);

	idx = 0;
	for(i = 0;i < num;i++){
		u32 tmp = 0;
		u32 sheimi_f;

		sheimi_f = FALSE;
		poke = &poke_tbl[idx];
		pp = PokeParty_GetMemberPointer(party,i);

		//タマゴは記録しない
		if(	(PokeParaGet(pp,ID_PARA_fusei_tamago_flag,NULL)) ||
			(PokeParaGet(pp,ID_PARA_tamago_flag,NULL))){
			continue;
		}
		poke->mons_no = PokeParaGet(pp,ID_PARA_monsno,NULL);
		poke->form_no = PokeParaGet(pp,ID_PARA_form_no,NULL);
		if(poke->mons_no == MONSNO_EURISU && poke->form_no == FORMNO_SHEIMI_FLOWER){
			PokeParaSheimiFormChange(pp, FORMNO_SHEIMI_NORMAL);	//シェイミは常にランドフォルムで記録
			poke->form_no = FORMNO_SHEIMI_NORMAL;
			sheimi_f = TRUE;
		}
		poke->item_no = PokeParaGet(pp,ID_PARA_item,NULL);
		poke->id_no = PokeParaGet(pp,ID_PARA_id_no,NULL);
		poke->personal_rnd = PokeParaGet(pp,ID_PARA_personal_rnd,NULL);
		poke->country_code = PokeParaGet(pp,ID_PARA_country_code,NULL);
		poke->tokusei = PokeParaGet(pp,ID_PARA_speabino,NULL);
		poke->natuki = PokeParaGet(pp,ID_PARA_friend,NULL);
		poke->level = PokeParaGet(pp,ID_PARA_level,NULL);
		{
			/*
				セーブバッファがMONS_NAME_SIZEきっちり分しかないので
				一度テンポラリに取得してから、再度コピー
			*/
			STRCODE buf[MONS_NAME_SIZE+EOM_SIZE];
			PokeParaGet(pp,ID_PARA_nickname,(u16*)buf);
			PM_strncpy(poke->nickname,buf,MONS_NAME_SIZE);
		}
		tmp = 0;
		for(j = 0;j < 4;j++){
			poke->waza[j] = PokeParaGet(pp,ID_PARA_waza1+j,NULL);
			tmp |= (PokeParaGet(pp,ID_PARA_pp_count1+j,NULL) << (2*j));
		}
		poke->pp_count = (0xFF & tmp);

		tmp = 0;
		for(j = 0;j < 6;j++){
			tmp |= (PokeParaGet(pp,ID_PARA_hp_rnd+j,NULL) << (5*j));
			poke->exp[j] = PokeParaGet(pp,ID_PARA_hp_exp+j,NULL);
		}
		poke->power_rnd = tmp;

		if(sheimi_f){
			PokeParaSheimiFormChange(pp, FORMNO_SHEIMI_FLOWER);	//シェイミを無理やりランドにしてたら元に戻す
		}
		if(++idx >= TR_HOUSE_POKE_MAX){
			break;
		}
	}
}

/**
 *	@brief	現在のセーブデータ中で、有効なトレーナーデータ数を返す
 *
 *	@param	th_sv	in:トレーナーハウス用セーブデータへのポインタ
 *	@retval	有効なトレーナーデータの数
 */
static int sub_GetEnableDataNum(TR_HOUSE_SAVE* th_sv)
{
	int i,num;
	for(i = 0,num = 0;i < TR_HOUSE_TRAINER_MAX;i++,num++){	//データは必ず前詰め
		if(!TrHouseTrainer_IsEnable(&th_sv->trainer[i])){
			break;
		}
	}
	return num;
}

/*
 *	@brief	現在あるデータの中から、押し出す(削除する)データindexを返す
 *
 *	@param	th_sv	in:トレーナーハウス用セーブデータへのポインタ
 *	@param	pl		in:レコードデータ中のプレイヤーデータへのポインタ
 *	@param	same_f	out:plと比較して同じプレイヤーのデータがあった場合にTRUEを格納して返す
 *
 *	@retval	削除するデータindexナンバー
 */
static int sub_CheckDeleteIndex(TR_HOUSE_SAVE* th_sv,TR_HOUSE_PLAYER* pl,BOOL* same_f)
{
	int i;

	for(i = 0;i < TR_HOUSE_TRAINER_MAX;i++){
		if(!TrHouseTrainer_IsEnable(&th_sv->trainer[i])){
			continue;
		}
		if(TrHousePlayer_IsSamePlayerData(&th_sv->trainer[i].player,pl)){
			*same_f = TRUE;
			return i;
		}
	}
	*same_f = FALSE;
	return 0;	//一番先頭を削除
}


#include "field/evwkdef.h"
#include "src/fielddata/script/saveflag.h"

///ハウスの各トレーナーに対応したタイムフラグのNoテーブル
static const u16 tr_house_battle_flags[] = {
	TMFLG_TR_HOUSE_TRAINER00,
	TMFLG_TR_HOUSE_TRAINER01,
	TMFLG_TR_HOUSE_TRAINER02,
	TMFLG_TR_HOUSE_TRAINER03,
	TMFLG_TR_HOUSE_TRAINER04,
	TMFLG_TR_HOUSE_TRAINER05,
	TMFLG_TR_HOUSE_TRAINER06,
	TMFLG_TR_HOUSE_TRAINER07,
	TMFLG_TR_HOUSE_TRAINER08,
	TMFLG_TR_HOUSE_TRAINER09,
};
SDK_COMPILER_ASSERT(NELEMS(tr_house_battle_flags) == TR_HOUSE_TRAINER_MAX);

/*
 *	@brief	トレーナーハウスでの対戦フラグ列を生成する
 *
 *	@param	savedata	in:セーブデータポインタ
 *	@param	array		out:TR_HOUSE_TRAINER_MAX*sizeof(u8)のサイズを持つ、タイムフラグを格納するバッファ
 *
 *	@com	その瞬間のその日対戦済みフラグの状態を取得する
 */
static void sub_makeFlagArray(SAVEDATA * savedata, u8 * array)
{
	int i;

	EVENTWORK * ev = SaveData_GetEventWork(savedata);
	OS_Printf("NOW TR HOUSE BATTLE FLAGS:");
	for (i = 0; i < TR_HOUSE_TRAINER_MAX; i++) {
		array[i] = EventWork_CheckEventFlag(ev, tr_house_battle_flags[i]);
		OS_Printf("%d:",array[i]);
	}
	OS_Printf("\n");
}
/*
 *	@brief	トレーナーハウスでの対戦フラグ列を反映する
 *
 *	@param	savedata	in:セーブデータポインタ
 *	@param	array		out:TR_HOUSE_TRAINER_MAX*sizeof(u8)のサイズを持つ、タイムフラグを格納するバッファ
 *
 *	@com	データ列の押し出し操作後に対戦済みフラグの状態も整合性を取る
 */
static void sub_updateFlagArray(SAVEDATA * savedata, const u8 * array)
{
	int i;

	EVENTWORK * ev = SaveData_GetEventWork(savedata);
	OS_Printf("UPDATE TR HOUSE BATTLE FLAGS:");
	for (i = 0; i < TR_HOUSE_TRAINER_MAX; i++) {
		if (array[i]) {
			EventWork_SetEventFlag(ev, tr_house_battle_flags[i]);
		} else {
			EventWork_ResetEventFlag(ev, tr_house_battle_flags[i]);
		}
		OS_Printf("%d:",array[i]);
	}
	OS_Printf("\n");
}

